package com.java.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.java.enums.LendStatusEnum;
import com.java.enums.ReturnMethodEnum;
import com.java.enums.TransTypeEnum;
import com.java.exceptioin.BusinessException;
import com.java.hfb.HfbConst;
import com.java.hfb.RequestHelper;
import com.java.mapper.*;
import com.java.pojo.bo.TransFlowBO;
import com.java.pojo.entity.*;
import com.java.pojo.vo.BorrowInfoApprovalVo;
import com.java.pojo.vo.BorrowerDetailVo;
import com.java.result.ResponseEnum;
import com.java.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.java.utils.*;
import org.joda.time.format.DateTimeFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.sql.Array;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 标的准备表 服务实现类
 * </p>
 *
 * @author Helen
 * @since 2021-07-08
 */
@Service
public class LendServiceImpl extends ServiceImpl<LendMapper, Lend> implements LendService {

    private static final Logger log1 = LoggerFactory.getLogger(LendServiceImpl.class);
    @Resource
    private LendMapper lendMapper;

    @Resource
    private DictService dictService;

    @Resource
    private BorrowerService borrowerService;

    @Resource
    private BorrowerMapper borrowerMapper;

    @Resource
    private UserInfoMapper userInfoMapper;

    @Resource
    private UserBindService userBindService;

    @Resource
    private TransFlowService transFlowService;

    @Resource
    private LendItemMapper lendItemMapper;


    @Resource
    private LendReturnService lendReturnService;

    @Resource
    private LendItemService lendItemService;

    @Resource
    private LendItemReturnService lendItemReturnService;



    //新增投资标的
    @Override
    public void createLend(BorrowInfoApprovalVo borrowInfoApprovalVo, BorrowInfo borrowInfo) {
        //填充数据
        Lend lend = new Lend();
        lend.setUserId(borrowInfo.getUserId());
        lend.setBorrowInfoId(borrowInfoApprovalVo.getId());
        //标的流水标号
        lend.setLendNo(LendNoUtils.getLendNo());
        lend.setTitle(borrowInfoApprovalVo.getTitle());
        lend.setAmount(borrowInfo.getAmount());
        lend.setPeriod(borrowInfo.getPeriod());
        //整数转bigDecimal
        lend.setLendYearRate(borrowInfoApprovalVo.getLendYearRate());
        lend.setServiceRate(borrowInfoApprovalVo.getServiceRate().divide(new BigDecimal(100)));
        lend.setReturnMethod(borrowInfo.getReturnMethod());
        lend.setLowestAmount(new BigDecimal(100));
        //新标的
        lend.setInvestAmount(new BigDecimal(0));
        lend.setInvestNum(0);
        //发布时间
        lend.setPublishDate(LocalDateTime.now());
        //起息日
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate localDate = LocalDate.parse(borrowInfoApprovalVo.getLendStartDate().toString(), dateTimeFormatter);
        lend.setLendStartDate(localDate);
        //结束日 起息日+期数
        LocalDate plusMonths = localDate.plusMonths(borrowInfo.getPeriod());
        lend.setLendEndDate(plusMonths);
        lend.setLendInfo(borrowInfoApprovalVo.getLendInfo());
        //平台预期收益 = 标的金额 * (年化利率/12 * 期数)
        BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(12), 8, BigDecimal.ROUND_DOWN);
        BigDecimal expectAmount = lend.getAmount().multiply(monthRate.multiply(new BigDecimal(lend.getPeriod())));
        lend.setExpectAmount(expectAmount);

        //实际收益
        lend.setRealAmount(new BigDecimal(0));
        //状态：
        lend.setStatus(LendStatusEnum.INVEST_RUN.getStatus());
        //审核时间
        lend.setCheckTime(LocalDateTime.now());
        lend.setCheckAdminId(1L);

        lendMapper.insert(lend);

    }

    @Override
    public List<Lend> selectList() {

        List<Lend> lends = lendMapper.selectList(null);
        lends.forEach(lend->{
            String returnMethod = dictService.getNameByParentDictCodeAndValue("returnMethod", lend.getReturnMethod());
            String msgByStatus = LendStatusEnum.getMsgByStatus(lend.getStatus());
            lend.getParam().put("returnMehod",returnMethod);
            lend.getParam().put("status",msgByStatus);
        });

        return lends;
    }

    @Override
    public Map<String, Object> getLendDetail(Long id) {
        Lend lend = lendMapper.selectById(id);

        //组装数据
        String returnMethod = dictService.getNameByParentDictCodeAndValue("returnMethod", lend.getReturnMethod());
        String status = LendStatusEnum.getMsgByStatus(lend.getStatus());
        lend.getParam().put("returnMethod", returnMethod);
        lend.getParam().put("status", status);

        //根据user_id获取借款人对象
        Long userId = lend.getUserId();
        QueryWrapper<Borrower> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id",userId);
        Borrower borrower = borrowerMapper.selectOne(wrapper);
        BorrowerDetailVo borrowerDetail = borrowerService.getBorrowerDetail(borrower.getId());

        //组装数据
        Map<String, Object> result = new HashMap<>();
        result.put("lend", lend);
        result.put("borrower", borrowerDetail);
        return result;
    }

    @Override
    public BigDecimal getInterestCount(BigDecimal invest, BigDecimal yearRate, Integer totalMonth, Integer returnMethod) {

        BigDecimal interestCount;

        if(returnMethod == ReturnMethodEnum.ONE.getMethod()){
            //等额本息
            interestCount = Amount1Helper.getInterestCount(invest, yearRate, totalMonth);
        }else if(returnMethod == ReturnMethodEnum.TWO.getMethod()){
            //等额本金
            interestCount = Amount2Helper.getInterestCount(invest, yearRate, totalMonth);
        }else if(returnMethod == ReturnMethodEnum.THREE.getMethod()){
            //每月还息 一次还本
            interestCount = Amount3Helper.getInterestCount(invest, yearRate, totalMonth);
        }else {
            //一次性还本付息
            interestCount = Amount4Helper.getInterestCount(invest, yearRate, totalMonth);
        }
        return interestCount;
    }

    //放款
    @Override
    public void makeLoan(Integer id) {

        Lend lend = lendMapper.selectById(id);

        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("agentId", HfbConst.AGENT_ID);
        //项目编号
        paramMap.put("agentProjectCode",lend.getLendNo());
        //流水编号
        String loanNo = LendNoUtils.getLoanNo();
        paramMap.put("agentBillNo",loanNo);
        //计算平台服务费 投资金额 * 服务月费率 * 期数
        BigDecimal ServiceMonRate = lend.getServiceRate().divide(new BigDecimal(12),8,BigDecimal.ROUND_DOWN);
        BigDecimal realAmount = lend.getInvestAmount().multiply(ServiceMonRate).multiply(new BigDecimal(lend.getPeriod()));
        paramMap.put("mchFee",realAmount);//商户手续费(平台实际收益)
        paramMap.put("timestamp", RequestHelper.getTimestamp());
        paramMap.put("sign",RequestHelper.getSign(paramMap));

        JSONObject jsonObject = RequestHelper.sendRequest(paramMap, HfbConst.MAKE_LOAD_URL);

        log1.info("jsonObject:"+jsonObject);

        String resultCode = jsonObject.getString("resultCode");
        if("0000".equals(resultCode)){
            log1.info("放款成功");
        }else{
            throw new BusinessException(jsonObject.getString("resultMsg"));
        }
        //更新标的相关的信息
        lend.setStatus(LendStatusEnum.PAY_RUN.getStatus());
        lend.setRealAmount(realAmount);
        lend.setLendStartDate(LocalDate.now());
        lendMapper.updateById(lend);
        //给借款人账号转入金额
        BigDecimal voteAmt = jsonObject.getBigDecimal("voteAmt");
        //获取借款人的bindCode
        String bindCode = userBindService.getBindCodeByUserId(lend.getUserId());
        userInfoMapper.updateAmount(bindCode,voteAmt,new BigDecimal(0));

        TransFlowBO transFlowBO;
        //增加借款人借到钱的交易流水
        transFlowBO = new TransFlowBO(
                loanNo,
                bindCode,
                voteAmt,
                TransTypeEnum.BORROW_BACK,
                "借款放款到账，编号"+lend.getLendNo()
        );
        transFlowService.saveTransFlow(transFlowBO);

        //解冻并扣除投资人金额
        QueryWrapper<LendItem> wrapper = new QueryWrapper<>();
        wrapper.eq("lend_id",lend.getId());
        List<LendItem> lendItemList = lendItemMapper.selectList(wrapper);
        lendItemList.forEach(lendItem -> {
            TransFlowBO transFlowBO1;
            String investBindCode = userBindService.getBindCodeByUserId(lendItem.getInvestUserId());
            userInfoMapper.updateAmount(investBindCode,new BigDecimal(0),lendItem.getInvestAmount().negate());
            //增加投资人交易流水
            transFlowBO1 = new TransFlowBO(
                    LendNoUtils.getTransNo(),
                    investBindCode,
                    lendItem.getInvestAmount(),
                    TransTypeEnum.INVEST_UNLOCK,
                    "项目放款,冻结资金转出,项目编号："+lend.getLendNo()+"项目名称："+lend.getTitle()
            );
            transFlowService.saveTransFlow(transFlowBO1);
        });
        //生成借款人还款计划和出借人回款计划

        //生成计划记录列表
        this.repaymentPlan(lend);
    }



    //还款计划列表
    private void repaymentPlan(Lend lend){
        //获取还款期数
        Integer period = lend.getPeriod();
        //生成一个还款计划集合
        ArrayList<LendReturn> LendReturnList = new ArrayList<>();
        //遍历期数创建每个还款计划对象
        for(int i=1;i<=period;i++){
            LendReturn lendReturn = new LendReturn();
            //设置还款计划对象基本属性
            lendReturn.setReturnNo(LendNoUtils.getReturnNo());
            lendReturn.setLendId(lend.getId());
            lendReturn.setBorrowInfoId(lend.getBorrowInfoId());
            lendReturn.setUserId(lend.getUserId());
            lendReturn.setAmount(lend.getAmount());
            //实际投资的金额
            lendReturn.setBaseAmount(lend.getInvestAmount());
            //当前的期数
            lendReturn.setCurrentPeriod(i);
            lendReturn.setLendYearRate(lend.getLendYearRate());
            lendReturn.setReturnMethod(lend.getReturnMethod());

            //手续费
            lendReturn.setFee(new BigDecimal(0));
            //计息开始的日期加上一个月
            lendReturn.setReturnDate(lend.getLendStartDate().plusMonths(i));
            lendReturn.setOverdue(false);
            //判断是否是最后一起还款
            if(i == period){
                lendReturn.setLast(true);
            }else{
                lendReturn.setLast(false);
            }
            //设置还款状态 (0-未归还 1-已归还)
            lendReturn.setStatus(0);
            LendReturnList.add(lendReturn);
        }
        //批量保存还款计划
        lendReturnService.saveBatch(LendReturnList);

        //生成期数和还款记录id对应的键值对集合
        Map<Integer, Long> lendReturnMap = LendReturnList.stream().collect(
                Collectors.toMap(LendReturn::getCurrentPeriod, LendReturn::getId)
        );

        //创建所有投资的所有回款记录列表
        List<LendItemReturn> lendItemReturnAllList = new ArrayList<>();
        //获取当前标的所有已支付的投资
        List<LendItem> lendItemList = lendItemService.selectByLendId(lend.getId(),1);

        for (LendItem lendItem : lendItemList) {
            //得到当前这笔投资的回款计划列表
            List<LendItemReturn> lendItemReturnList =  this.returnInvest(lendItem.getId(),lendReturnMap,lend);
            //将当前的回款计划放到所有投资计划列表里面
            lendItemReturnAllList.addAll(lendItemReturnList);
        }

        for (LendReturn lendReturn : LendReturnList) {

            BigDecimal sumPrincipal = lendItemReturnAllList.stream()
                    //过滤条件：当回款计划中的还款计划id == 当前还款计划id的时候
                    .filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    //将所有回款计划中计算的每月应收本金相加
                    .map(LendItemReturn::getPrincipal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal sumInterest = lendItemReturnAllList.stream()
                    .filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    .map(LendItemReturn::getInterest)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal sumTotal = lendItemReturnAllList.stream()
                    .filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    .map(LendItemReturn::getTotal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);


            lendReturn.setPrincipal(sumPrincipal); //每期还款本金
            lendReturn.setInterest(sumInterest); //每期还款利息
            lendReturn.setTotal(sumTotal); //每期还款本息
        }

        lendReturnService.updateBatchById(LendReturnList);

    }

    private List<LendItemReturn> returnInvest(Long lendItemId, Map<Integer, Long> lendReturnMap, Lend lend) {

        LendItem lendItem = lendItemService.getById(lendItemId);
        //投资金额
        BigDecimal amount = lendItem.getInvestAmount();
        //年化利率
        BigDecimal yearRate = lendItem.getLendYearRate();
        //期数
        Integer totalMonth = lend.getPeriod();

        Map<Integer, BigDecimal> mapInterest = null;  //还款期数 -> 利息
        Map<Integer, BigDecimal> mapPrincipal = null; //还款期数 -> 本金

        //根据还款方式计算本金和利息
        if (lend.getReturnMethod().intValue() == ReturnMethodEnum.ONE.getMethod()) {
            //利息
            mapInterest = Amount1Helper.getPerMonthInterest(amount, yearRate, totalMonth);
            //本金
            mapPrincipal = Amount1Helper.getPerMonthPrincipal(amount, yearRate, totalMonth);
        } else if (lend.getReturnMethod().intValue() == ReturnMethodEnum.TWO.getMethod()) {
            mapInterest = Amount2Helper.getPerMonthInterest(amount, yearRate, totalMonth);
            mapPrincipal = Amount2Helper.getPerMonthPrincipal(amount, yearRate, totalMonth);
        } else if (lend.getReturnMethod().intValue() == ReturnMethodEnum.THREE.getMethod()) {
            mapInterest = Amount3Helper.getPerMonthInterest(amount, yearRate, totalMonth);
            mapPrincipal = Amount3Helper.getPerMonthPrincipal(amount, yearRate, totalMonth);
        } else {
            mapInterest = Amount4Helper.getPerMonthInterest(amount, yearRate, totalMonth);
            mapPrincipal = Amount4Helper.getPerMonthPrincipal(amount, yearRate, totalMonth);
        }

        //创建回款计划列表
        List<LendItemReturn> lendItemReturnList = new ArrayList<>();
        for (Map.Entry<Integer, BigDecimal> entry : mapInterest.entrySet()) {
            Integer currentPeriod = entry.getKey();
            Long lendReturnId = lendReturnMap.get(currentPeriod);
            //创建回款记录
            LendItemReturn lendItemReturn = new LendItemReturn();
            //将还款记录关联到回款记录
            lendItemReturn.setLendReturnId(lendReturnId);
            //设置回款记录的基本属性
            lendItemReturn.setLendItemId(lendItemId);
            lendItemReturn.setInvestUserId(lendItem.getInvestUserId());
            lendItemReturn.setLendId(lendItem.getLendId());
            lendItemReturn.setInvestAmount(lendItem.getInvestAmount());
            lendItemReturn.setLendYearRate(lend.getLendYearRate());
            lendItemReturn.setCurrentPeriod(currentPeriod);
            lendItemReturn.setReturnMethod(lend.getReturnMethod());

            //计算回款本金利息和总额
            if(currentPeriod.intValue() == lend.getPeriod().intValue()){//最后一期
                //先把list里面除了最后一期的金额计算出来
                BigDecimal sumPrincipal = lendItemReturnList.stream()
                        .map(LendItemReturn::getPrincipal)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                //最后一期应还本金 = 用当前投资人的总投资金额 - 除了最后一期前面期数计算出来的所有的应还本金
                BigDecimal lastPrincipal = lendItem.getInvestAmount().subtract(sumPrincipal);
                lendItemReturn.setPrincipal(lastPrincipal);

                //
                BigDecimal sumInterest = lendItemReturnList.stream()
                        .map(LendItemReturn::getInterest)
                        .reduce(BigDecimal.ZERO, BigDecimal::add);
                //最后一期应还本金 = 用当前投资人的总投资金额 - 除了最后一期前面期数计算出来的所有的应还本金
                BigDecimal lastInterest = lendItem.getExpectAmount().subtract(sumInterest);
                lendItemReturn.setInterest(lastInterest);

            }else{//非最后一期
                lendItemReturn.setPrincipal(mapPrincipal.get(currentPeriod));
                lendItemReturn.setInterest(mapInterest.get(currentPeriod));
            }
            //回款总金额
            lendItemReturn.setTotal(lendItemReturn.getPrincipal().add(lendItemReturn.getInterest()));
            //设置其他属性
            lendItemReturn.setFee(new BigDecimal("0"));
            lendItemReturn.setReturnDate(lend.getLendStartDate().plusMonths(currentPeriod));
            //是否逾期，默认未逾期
            lendItemReturn.setOverdue(false);
            lendItemReturn.setStatus(0);

            lendItemReturnList.add(lendItemReturn);
        }
        lendItemReturnService.saveBatch(lendItemReturnList);

        return lendItemReturnList;
    }

}
